/******************************************************************************/
/* File: udpTrafficRecver.cpp                                                 */
/******************************************************************************/

#include "udpTrafficRecver.h"

UDPTrafficRecver::UDPTrafficRecver(UDPConnectedSocket *pus, ThreadConf *ptc) 
                : pUdpSock(pus), pThreadConf(ptc) {
    
    pRecvBuf = new char[DataStore::generalSetting.iUDPBufferLength];                    
}

UDPTrafficRecver::~UDPTrafficRecver() {
    
    if (pRecvBuf) {
        delete pRecvBuf;
        pRecvBuf = NULL;
    }
}

int UDPTrafficRecver::RecvPacket(int64_t *iPacketID, int length, timeval *sentTime) {
    uint64_t packetSec, packetUSec;
    int64_t tmpPacketID;
    int iRecvBytes = pUdpSock->Recv(pRecvBuf, length, 0);
    
    // 从数据包中提取出信息，包括发送时间和包序号
    memcpy(&packetSec, pRecvBuf, sizeof(packetSec));
    memcpy(&packetUSec, pRecvBuf + 4, sizeof(packetUSec));
    memcpy(&tmpPacketID, pRecvBuf + 8, sizeof(tmpPacketID));
    packetSec = ntohl(packetSec);
    packetUSec = ntohl(packetUSec);
    tmpPacketID = ntohl(tmpPacketID);
    
    *iPacketID = tmpPacketID;
    sentTime->tv_sec = packetSec;
    sentTime->tv_usec = packetUSec;

    return iRecvBytes;
}

void UDPTrafficRecver::ExitCleanUp() {
    pThreadConf->SetStoppedFlag(1);
}

void UDPTrafficRecver::Run() {
    
    std::cout << "Running udp traffic receiver instance....\n";
    
    int updateInterval = DataStore::generalSetting.iUpdateInterval;
    int reportInterval = DataStore::generalSetting.iReportInterval;
    int numOnce = DataStore::generalSetting.iNumOnce;
    int length = DataStore::generalSetting.iUDPBufferLength;
    
    /*
    // 设置接收超时标志，以妨一直阻塞在recv上
    struct timeval recvTimeout;
    recvTimeout.tv_sec = 0;
    recvTimeout.tv_usec = updateInterval;
    
    try {
        pUdpSock->SetSockOpt(SOL_SOCKET, SO_RCVTIMEO, (char *)&recvTimeout, sizeof(recvTimeout));
    } 
    catch (SocketException se) {
        se.PrintException();
        ExitCleanUp();
        return;
    }
    */
    // basic info
    double startTime = 0.0;
    double endTime = 0.0;
    double bandwidth = 0.0;
    
    // jitter 
    double jitter = 0.0;
    double prevTransit = 0.0;
    
    // recv bytes
    int64_t totBytes = 0;
    int64_t curBytes = 0;
    int64_t packetID = 0;
    
    // packet count
    int64_t packetCount = 0;
    int64_t packetError = 0;
    int64_t packetOutOfOrder = 0;
    
    struct timeval lastReportTime, nowTime, reportTime;
    
    gettimeofday(&nowTime, NULL);

    lastReportTime = nowTime;
    reportTime = nowTime;
    UtilClass::TimevalAddLong(&reportTime, reportInterval);
    
    // 接收数据包相关
    int64_t tmpID;
    timeval sentTime, arrivalTime;
    
    pThreadConf->SetStartedFlag(1);
    
    while(true) {
        
        // 检查停止标志 --- 为了更好地解决，可以再做最后一次统计，然后再退出 
        if (pThreadConf->GetHaltFlag() == 1) {
            ExitCleanUp();
            return;
        }
        
        int count = 0;
        while(count < numOnce) {
            try {
                curBytes = RecvPacket(&tmpID, length, &sentTime);
                count++;
            } catch (SocketException se) {
                se.PrintException();
                std::cout << "UDP traffic receiver shutdown for remote disconnected.\n";
                ExitCleanUp();
                return;
            }
            
            totBytes += curBytes;
            packetCount++;
            
            // 统计packetError
            if(tmpID >= packetID + 1) {
                // 错误
                if(tmpID > packetID + 1)
                    packetError += tmpID - packetID - 1;
                    
                packetID = tmpID;
            } 
            else {
                packetOutOfOrder++;
            }
            
            // 计算jitter
            gettimeofday(&arrivalTime, NULL);
            double transit = UtilClass::TimevalDiff(arrivalTime, sentTime) / MILLION;
            double delta = transit - prevTransit;
            prevTransit = transit;
            
            if (delta < 0) 
                delta = -delta;
            
            // This is NOT the way to calculate jitter
            // J = |(R1 - S1) - (R0 - S0)| [/ number of packets, for average]
            jitter += (delta - jitter) / 16.0;
        }
        
         // 检查是否需要上传统计报告
        gettimeofday(&nowTime, NULL);
        if (UtilClass::TimevalGreaterThan(nowTime, reportTime)) {
            double diffUsecs = UtilClass::TimevalDiff(nowTime, lastReportTime);
            double factIntv = diffUsecs / MILLION;
            endTime += factIntv;
            bandwidth = totBytes / factIntv;
            UploadReport(startTime, endTime, totBytes, bandwidth, packetCount, jitter, packetError);
            
            // 重置数据
            startTime = endTime;
            totBytes = 0;
            bandwidth = 0.0;
            packetError = 0;
            packetCount = 0;
            
            // 重新设置reportTime
            reportTime = nowTime;
            UtilClass::TimevalAddLong(&reportTime, reportInterval);
            lastReportTime = nowTime;
        }
    }    
}

void UDPTrafficRecver::UploadReport(double begin, double end, int64_t transfer, double bandwidth, 
                                    int64_t totalPkts, double jitter, int64_t lostPkts) {
    
    std::cout << "Upload udp report...\n";
    
    UdpReport *pur = new UdpReport();
    pur->fBeginTime = begin;
    pur->fEndTime = end;
    pur->fInterval = end - begin;
    pur->iTransfer = transfer;
    pur->fBandwidth = bandwidth;
    pur->fJitter = jitter;
    pur->iPacketLost = lostPkts;
    pur->iPacketTotal = totalPkts;
    pur->fLoss = lostPkts / (double)totalPkts;
    
    Report *pr = new Report();
    pr->iRole = 2; // -- Recver
    pr->iProto = 2; // --UDP

    char *rip = pUdpSock->RemoteHostName();
    pr->sSenderIP = strdup(rip);
    pr->iSenderPort = pUdpSock->RemotePortNumber();
    char *lip = pUdpSock->LocalHostName();
    pr->sRecverIP = strdup(lip);
    pr->iRecverPort = pUdpSock->LocalPortNumber();
    pr->pUdpReport = pur;
    
    Message *pm = new Message();
    pm->iMessageType = 3;
    pm->pReport = pr;
    
    pThreadConf->pMQ->PushMessage(pm);
}

/******************************************************************************/
/* End File: udpTrafficRecver.cpp                                             */
/******************************************************************************/